package com.peking.donations.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.peking.donations.base.service.BizServiceException;
import com.peking.donations.base.util.RandomUtil;
import com.peking.donations.constants.ACTCacheNames;
import com.peking.donations.dao.CallOrderDao;
import com.peking.donations.dao.DonationSavingDao;
import com.peking.donations.model.CallOrder;
import com.peking.donations.model.DonationSaving;
import com.peking.donations.model.DonationUser;
import com.peking.donations.service.BizService;
import com.peking.donations.service.UserService;
import com.peking.donations.vo.GiftCallMember;
import com.peking.donations.vo.GiftCallVo;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * Created by wyz on 16/4/27.
 */
@Service("bizService")
@Transactional(readOnly = true)
public class BizServiceImpl implements BizService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private DonationSavingDao savingDao;

    @Autowired
    private CallOrderDao callOrderDao;

    @Autowired
    private UserService userService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    @Transactional(readOnly = false)
    public DonationSaving createDonationSaving(DonationSaving saving) {
        saving.setTransactionId(buildTranscationId());
        saving.setSavingStatus(DonationSaving.SAVING_STATUS_IN_PROCESS);
        return savingDao.save(saving);
    }

    @Override
    @Transactional(readOnly = false)
    public DonationSaving updateSavingStatus(DonationSaving saving) {
        DonationSaving object = savingDao.findByTransactionId(saving.getTransactionId());
        object.setSavingStatus(DonationSaving.SAVING_STATUS_SUCCESS);
        object.setFromTransactionId(saving.getFromTransactionId());
        return savingDao.save(object);
    }

    @Override
    public int accountUserShare(int userId) {
        return savingDao.accountUserShare(userId);
    }

    @Override
    public int accountCallOrderShare(int orderId) {
        return savingDao.accountCallOrderShare(orderId);
    }

    @Override
    public DonationSaving findSavingByTransactionId(String transactionId) {
        return savingDao.findByTransactionId(transactionId);
    }

    @Override
    public Map<String, Integer> countTotal() {
        Object giftCount = redisTemplate.opsForHash().get(ACTCacheNames.ACT_GlOAB_STATISTIC.name(), "giftCount");
        Object userCount = redisTemplate.opsForHash().get(ACTCacheNames.ACT_GlOAB_STATISTIC.name(), "joins");
        HashMap<String, Integer> map = Maps.newHashMap();
        map.put("giftCount", null == giftCount ? 0 : Integer.parseInt(giftCount.toString()));
        map.put("userCount", null == userCount ? 0 : Integer.parseInt(userCount.toString()));
        return map;
    }

    @Override
    @Transactional(readOnly = false)
    public CallOrder saveCallOrder(CallOrder callOrder) throws BizServiceException {
        CallOrder object = new CallOrder();

        List<CallOrder> callOrders = callOrderDao.findByName(callOrder.getName());
        if (!CollectionUtils.isEmpty(callOrders)) {
            throw new BizServiceException("召集令已存在.");
        }

        object.setUserId(callOrder.getUserId());
        object.setRealName(callOrder.getRealName());
        object.setName(callOrder.getName());
        object.setTotalShare(callOrder.getTotalShare());
        object = callOrderDao.save(object);

        redisTemplate.opsForHash().put(ACTCacheNames.CALL_ORDER.name(), object.getId() + "",
                JSONObject.toJSONString(object));
        redisTemplate.opsForZSet().add(ACTCacheNames.CALl_ORDER_TOTAL.name(), object.getId() + "", 0);
        return object;
    }

    @Override
    public List<GiftCallVo> findGiftCallTop10() {
        Set<TypedTuple<String>> s = redisTemplate.opsForZSet()
                .reverseRangeWithScores(ACTCacheNames.CALl_ORDER_TOTAL.name(), 0, 9);
        List<GiftCallVo> retList = Lists.newArrayList();

        for (TypedTuple<String> typedTuple : s) {
            GiftCallVo vo = new GiftCallVo();
            int callId = Integer.parseInt(typedTuple.getValue());
            Object o = redisTemplate.opsForHash().get(ACTCacheNames.CALL_ORDER.name(), callId + "");
            CallOrder callOrder = JSONObject.parseObject(o.toString(), CallOrder.class);
            vo.setName(callOrder.getName());
            vo.setGiftCount(typedTuple.getScore().intValue());
            vo.setGiftBid(callOrder.getTotalShare());
            retList.add(vo);
        }
        return retList;
    }

    @Override
    public GiftCallVo getGiftCall(Integer gcId) {
        Object o = redisTemplate.opsForHash().get(ACTCacheNames.CALL_ORDER.name(), gcId + "");
        CallOrder callOrder = JSONObject.parseObject(o.toString(), CallOrder.class);

        GiftCallVo vo = new GiftCallVo();
        vo.setName(callOrder.getName());
        vo.setGiftBid(callOrder.getTotalShare());

        // 已召集
        Double score = redisTemplate.opsForZSet().score(ACTCacheNames.CALl_ORDER_TOTAL.name(), gcId + "");
        if (null == score) {
            score = 0.0;
        }
        vo.setGiftCount(score.intValue());

        // 参与人数
        int joinCount = savingDao.countCallOrderJoins(gcId);
        vo.setJoinUserNum(joinCount);

        return vo;
    }

    @Override
    public Map<String, Integer> findGiftCallSorts(Integer gcId) {
        Long rank = redisTemplate.opsForZSet().reverseRank(ACTCacheNames.CALl_ORDER_TOTAL.name(), gcId + "");
        if (null == rank) {
            rank = 0l;
        }
        Map<String, Integer> sortMap = Maps.newHashMap();
        sortMap.put("timeRank", gcId);
        sortMap.put("giftRank", rank.intValue() + 1);
        return sortMap;
    }

    @Override
    public List<GiftCallMember> findGiftCallMembersTop10(Integer gcId, int page) {

        Set<TypedTuple<String>> membersSet = redisTemplate.opsForZSet().reverseRangeWithScores(
                ACTCacheNames.OWNER_CALL_ORDER_MEMBERS.name() + "_" + gcId, 10 * (page - 1), 10 * page - 1);
        List<GiftCallMember> members = Lists.newArrayList();
        for (TypedTuple<String> stringTypedTuple : membersSet) {
            GiftCallMember member = new GiftCallMember();
            String value = stringTypedTuple.getValue();
            // String[] split = value.split("_");
            // member.setName(split[1]);
            Object user = redisTemplate.opsForHash().get(ACTCacheNames.ACT_DONAION_USER.name(), value);
            if (user == null) {
                member.setName("神秘人");
            } else {
                DonationUser donationUser =  JSONObject.parseObject(user.toString(), DonationUser.class);
                String realName="神秘人";
                if(!Strings.isNullOrEmpty(donationUser.getRealName())){
                    realName=  donationUser.getRealName();
                }
                member.setName(realName);
            }
            member.setGiftCount(stringTypedTuple.getScore().intValue());
            members.add(member);
        }
        return members;
    }

    private String buildTranscationId() {
        String format = DateFormatUtils.format(Calendar.getInstance(), "yyMMddHHmmssSSS");
        return "PKU_T" + format + RandomUtil.randNumberFixed(4);
    }

    @Override
    public Map<String, Integer> findDepartmentOrder(int userId) {
        DonationUser user = userService.findById(userId);
        Map<String, Integer> map = Maps.newHashMap();
        if (user == null) {
            return map;
        }
        String object = (String) redisTemplate.opsForHash().get(ACTCacheNames.ACT_GRADE_ORDER.name(),
                user.getGrade() + "_" + user.getDepartmentCode() + "_userId");
        map.put("departmentCount", object == null ? 0 : Integer.valueOf(object));
        return map;
    }

    @Override
    public CallOrder findCallOrderById(int orderId) {
        return callOrderDao.findOne(orderId);
    }
}
